#include "http_conn.h"
#include "config.h"

int http_conn::m_epfd = -1;
int http_conn::m_user_count = 0;

//设置非阻塞
void setnonblocking(int fd)
{
    int old_flag = fcntl(fd, F_GETFL);
    int newflag = old_flag | O_NONBLOCK;
    fcntl(fd, F_SETFL, newflag);
}

//添加文件描述符到epoll中
void addfd(int epfd, int fd, bool one_shot)
{
    epoll_event event;
    event.events = EPOLLIN | EPOLLRDHUP; // epollhup对方连接断开会触发
    event.data.fd = fd;

    if (one_shot)
    {
        event.events | EPOLLONESHOT;
    }

    epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
    //设置文件描述符非阻塞
    setnonblocking(fd);
}

//从epoll中删除文件描述符

void removefd(int epfd, int fd)
{

    epoll_ctl(epfd, EPOLL_CTL_DEL, fd, 0);
    close(fd);
}

//修改文件描述符，确保下一次刻度时，epollin能够被触发
void modfd(int epfd, int fd, int ev)
{

    epoll_event event;
    event.data.fd = fd;
    event.events = ev | EPOLLONESHOT | EPOLLRDHUP;

    epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &event);
}
//初始化连接
void http_conn::init(int sockfd, const sockaddr_in &addr)
{

    m_sockfd = sockfd;
    m_addr = addr;

    //设置端口复用
    int reuse = 1;
    setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    addfd(m_epfd, m_sockfd, true);
    m_user_count++; //总用户数加1
    init();
}

void http_conn::init()
{

    


    m_check_state = CHECK_STATE_REQUESTLINE; //初始状态为c解析请求首行
    m_checked_index = 0;
    m_start_line = 0;
    m_read_idx = 0;

    m_method = GET;
    m_url = 0;
    m_version = 0;
    m_linger = false;

    bzero(m_read_buf, READ_BUF_SIZE);
}

//关闭连接

void http_conn::close_conn()
{

    if (m_sockfd != -1)
    {
        removefd(m_epfd, m_sockfd);
        m_sockfd = -1;
        m_user_count--;
    }
}

bool http_conn::read() //非阻塞读
{
    std::cout << "一次性读完所有数据" << std::endl;

    if (m_read_idx >= READ_BUF_SIZE)
    {
        return false;
    }

    int bytes_read = 0;
    while (true)
    {
        bytes_read = recv(m_sockfd, m_read_buf + m_read_idx, READ_BUF_SIZE - m_read_idx, 0);
        if (bytes_read == -1)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                break;
            }
            return false;
        }
        else if (bytes_read == 0)
        {
            //对方关闭
            return false;
        }

        m_read_idx += bytes_read;
    }
    std::cout << "读取到数据\t" << m_read_buf << std::endl;
    return true;
}

bool http_conn::write() //非阻塞写
{
    int temp=0;
    int bytes_have_send=0;//已经发送的字节
    int bytes_to_send=m_write_idx;//将要发送的字节

    if(bytes_to_send==0)
    {
        modfd(m_epfd,m_sockfd,EPOLLIN);
        init();
        return true;
    }

    while(1)
    {
        temp=writev(m_sockfd,m_iv,m_iv_count);
        if(temp<=-1)
        {
            if(errno==EAGAIN)
            {
                modfd(m_epfd,m_sockfd,EPOLLOUT);
                return true;
            }
            unmap();
            return false;
        }
        bytes_to_send-=temp;
        bytes_have_send+=temp;
        if(bytes_to_send<=bytes_have_send)
        {
            unmap();
            if(m_linger)
            {
                init();
                modfd(m_epfd,m_sockfd,EPOLLIN);
                return true;
            }else
            {
                modfd(m_epfd,m_sockfd,EPOLLIN);
                return false;
            }
        }
    }

    std::cout << "一次写完数据\t" << std::endl;
    return true;
}

http_conn::HTTP_CODE http_conn::process_read()
{

    LINE_STATUS line_status = LINE_OK;
    HTTP_CODE ret = NO_REQUEST;

    char *text{0};
    //表示解析这行数据是正常
    while (((m_check_state == CHECK_STATE_CONTENT) && (line_status == LINE_OK)) || ((line_status = parse_line()) == LINE_OK))
    {

        //解析到了一行完整的数据
        text = get_line();

        m_start_line = m_checked_index;
        std::cout << "get 1 http line:" << text << std::endl;

        switch (m_check_state)
        {
        case CHECK_STATE_REQUESTLINE:

            ret = parse_request_line(text);
            if (ret == BAD_REQUEST)
            {
                return BAD_REQUEST;
            }
            break;

        case CHECK_STATE_HEADER:
            ret = parse_headers(text);
            if (ret == BAD_REQUEST)
            {
                return BAD_REQUEST;
            }
            else if (ret == GET_REQUEST)
            {
                return do_request();
            }

            break;

        case CHECK_STATE_CONTENT:
            ret = parse_content(text);
            if (ret == GET_REQUEST)
            {
                return do_request();
            }
            line_status = LINE_OPEN; //行数据尚且不完整

            break;
        default:

            return INTERNAL_ERROR;
        }
    }

    return NO_REQUEST;
}

//解析请求行，获得请求方法，目标url http版本
http_conn::HTTP_CODE http_conn::parse_request_line(char *text)
{

    m_url = strpbrk(text, "\t");
    *m_url++ = '\0';
    char *method = text;

    if (strcasecmp(method, "GET") == 0)
    {
        m_method = GET;
    }
    else
    {
        return BAD_REQUEST;
    }

    m_version = strpbrk(m_url, "\t");
    if (!m_version)
    {
        return BAD_REQUEST;
    }

    *m_version++ = '\0';
    if (strcasecmp(m_version, "HTTP/1.1") != 0)
    {
        return BAD_REQUEST;
    }

    if (strncasecmp(m_url, "http://", 7) == 0)
    {
        m_url += 7;

        m_url = strchr(m_url, '/');
    }

    if (!m_url || m_url[0] != '/')
    {
        return BAD_REQUEST;
    }

    //主状态机
    m_check_state = CHECK_STATE_HEADER; //检测请求头

    return NO_REQUEST;
}
//解析请求头
http_conn::HTTP_CODE http_conn::parse_headers(char *text)
{
    //遇到空行表示头部字段解析完毕
    if (text[0] == '\0')
    {
        if (m_content_length != 0)
        {
            m_check_state = CHECK_STATE_CONTENT;
            return NO_REQUEST;
        }
        return GET_REQUEST;
    }
    else if (strncasecmp(text, "Connection:", 11) == 0)
    {
        text += 11;
        text += strspn(text, "\t");
        if (strncasecmp(text, "keep-alive", 15) == 0)
        {
            m_linger = true;
        }
    }
    else if (strncasecmp(text, "Content-Length", 15) == 0)
    {
        text += 15;
        text += strspn(text, "\t");
        m_content_length = atol(text);
    }
    else if (strncasecmp(text, "Host", 5) == 0)
    {
        text += 5;
        text += strspn(text, "\t");
        m_host = text;
    }
    else
    {
        std::cout << "oop unknow header:" << text << std::endl;
    }

    return NO_REQUEST;
}

//解析请求体
http_conn::HTTP_CODE http_conn::parse_content(char *text)
{
    if (m_read_idx >= (m_content_length + m_checked_index))
    {
        text[m_content_length] = '\0';
        return GET_REQUEST;
    }

    return NO_REQUEST;
}

http_conn::LINE_STATUS http_conn::parse_line()
{
    char temp;
    for (; m_checked_index < m_read_idx; ++m_checked_index)
    {
        temp = m_read_buf[m_checked_index];

        if (temp == '\r')
        {
            if ((m_checked_index + 1) == m_read_idx)
            {
                return LINE_OPEN;
            }
            else if (m_read_buf[m_check_state + 1] == '\n')
            {
                m_read_buf[m_checked_index++] = '\0';
                m_read_buf[m_read_idx++] = '\0';
                return LINE_OK;
            }
            return LINE_BAD;
        }
        else if (temp == '\n')
        {

            if ((m_checked_index > 1) && (m_read_buf[m_checked_index - 1] == '\r'))
            {
                m_read_buf[m_checked_index - 1] = '\0';
                m_read_buf[m_checked_index++] = '\0';
                return LINE_OK;
            }
        }
    }

    return LINE_OK;
}

http_conn::HTTP_CODE http_conn::do_request()
{
    strcpy(m_real_file, doc_root);
    int len = strlen(doc_root);

    strncpy(m_real_file + len, m_url, FILENAME_LEN - len - 1);
    //获取m_real_fiel状态-1失败

    if (stat(m_real_file, &m_file_stat) < 0)
    {
        return NO_RESOURCE;
    }

    //判断是否有足够权限
    if (!(m_file_stat.st_mode & S_IROTH))
    {
        return FORBIDDEN_REQUEST;
    }
    if (S_ISDIR(m_file_stat.st_mode))
    {
        return BAD_REQUEST;
    }

    int fd = open(m_real_file, O_RDONLY);

    m_file_address = (char *)mmap(0, m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);

    close(fd);
    return FILE_REQUEST;
}

//存储映射区
void http_conn::unmap()
{

    if (m_file_address)
    {
        munmap(m_file_address, m_file_stat.st_size);
        m_file_address = 0;
    }
}

bool http_conn::process_write(http_conn::HTTP_CODE ret)
{

    switch (ret)
    {
    case http_conn::HTTP_CODE::INTERNAL_ERROR:
        add_status_line(500, error_500_title);
        if (!add_content(error_500_form))
        {
            return false;
        }
        break;
    case http_conn::HTTP_CODE::BAD_REQUEST:
        add_status_line(400, error_400_title);
        add_headers(strlen(error_400_form));
        if (!add_content(error_400_form))
        {
            return false;
        }
        break;
    case http_conn::HTTP_CODE::NO_RESOURCE:
        add_status_line(404, error_404_form);
        add_headers(strlen(error_404_form));
        if (!add_content(error_404_form))
        {
            return false;
        }
        break;
    case http_conn::HTTP_CODE::FORBIDDEN_REQUEST:
        add_status_line(403, error_403_form);
        add_headers(strlen(error_403_form));
        if (!add_content(error_403_form))
        {
            return false;
        }
        break;

    case http_conn::HTTP_CODE::FILE_REQUEST:
        add_status_line(200, ok_200_title);
        if (http_conn::m_file_stat.st_size != 0)
        {
            add_headers(m_file_stat.st_size);
            m_iv[0].iov_base = m_write_buf;
            m_iv[0].iov_len = m_write_idx;
            m_iv[1].iov_base = m_file_address;
            m_iv[1].iov_len = m_file_stat.st_size;
            m_iv_count = 2;
            return true;
        }
        else
        {
            const char *ok_string = "<html><body></body></html>";
            add_headers(strlen(ok_string));
            if (!add_content(ok_string))
            {
                return false;
            }
        }

        break;

    default:
        return false;
    }


    m_iv[0].iov_base=m_write_buf;
    m_iv[0].iov_len=m_write_idx;
    return true;
}

bool http_conn::add_response(const char *format, ...)
{
    if (m_write_idx>=WRITE_BUF_SIZE)
    {
        return false;
    }
    va_list arg_list;
    va_start(arg_list,format);
    int len=vsnprintf(m_write_buf+m_write_idx,WRITE_BUF_SIZE-1-m_write_idx,format,arg_list);

    if(len>=(WRITE_BUF_SIZE-1-m_write_idx))
    {
        return false;
    }
    m_write_idx+=len;
    va_end(arg_list);
    return true;


}

bool http_conn::add_content(const char *content)
{
    return add_response("%s",content);
}

bool add_status_line(int status, const char *title);
bool http_conn::add_headers(int content_length)
{
   
    add_content_length(content_length);
    add_linger();
    add_blank_line();
    return true;

}

 bool http_conn::add_content_length(int content_len)
 {

     return add_response("Content-Length:%d\r\n",content_len);

 }



bool http_conn::add_linger()
{
    return add_response("Connection:%s\r\n",(m_linger==true)?"keep-alive":"close");
}

bool http_conn::add_blank_line()
{
    return add_response("%s","\r\n");


}

//由线程池中的工作线程调用，这是处理http请求的入口函数
void http_conn::process()
{

    //解析http请求
    std::cout << "parse request and response" << std::endl;
    HTTP_CODE read_ret = process_read();
    if (read_ret == NO_REQUEST)
    {
        modfd(m_epfd, m_sockfd, EPOLLIN);
        return;
    }
    //生成响应
    bool write_ret = process_write(read_ret);
    if (!write_ret)
    {
        close_conn();
    }
    modfd(m_epfd, m_sockfd, EPOLLOUT);
}

 bool http_conn::add_status_line(int status,const char*title){

     return add_response("%s %d %s\r\n","HTTP/1.1",status,title);


 }